package com.sparrow.core.asm;

import javax.tools.*;

import com.sparrow.utils.FileUtil;

import java.util.ArrayList;
import java.util.List;
import java.util.Locale;
import java.net.URL;
import java.net.URLClassLoader;
import java.nio.charset.Charset;
import java.io.File;

public class DynamicEngine {
	private static DynamicEngine ourInstance = new DynamicEngine();

	public static DynamicEngine getInstance() {
		return ourInstance;
	}

	private URLClassLoader classLoader;
	@SuppressWarnings("unused")
	private String classpath;

	private DynamicEngine() {
		this.classLoader = (URLClassLoader) this.getClass().getClassLoader();
		this.buildClassPath();
	}

	private void buildClassPath() {
		this.classpath = null;
		StringBuilder sb = new StringBuilder();
		for (URL url : this.classLoader.getURLs()) {
			String p = url.getFile();
			sb.append(p).append(File.pathSeparator);
		}
		this.classpath = sb.toString();
	}

	public Object javaCodeToObject(String fullClassName, String javaCode)
			throws IllegalAccessException, InstantiationException {
		long start = System.currentTimeMillis();
		Object instance = null;
		// 获取系统的java 编译器
		JavaCompiler compiler = ToolProvider.getSystemJavaCompiler();
		// 诊断 收集器
		DiagnosticCollector<JavaFileObject> diagnostics = new DiagnosticCollector<JavaFileObject>();
		ClassFileManager fileManager = new ClassFileManager(
				compiler.getStandardFileManager(diagnostics, Locale.CHINA,
						Charset.forName("utf-8")));

		List<JavaFileObject> jfiles = new ArrayList<JavaFileObject>();
		jfiles.add(new JavaSourceFileObject(fullClassName, javaCode));

		/*
		 * 
		 * 2.用法：javac <选项> <源文件>
		 * 
		 * 3.其中，可能的选项包括：
		 * 
		 * 4.-g 生成所有调试信息
		 * 
		 * 5.-g:none 不生成任何调试信息
		 * 
		 * 6.-g:{lines,vars,source} 只生成某些调试信息
		 * 
		 * 7.-nowarn 不生成任何警告
		 * 
		 * 8.-verbose 输出有关编译器正在执行的操作的消息
		 * 
		 * 9.-deprecation 输出使用已过时的 API 的源位置
		 * 
		 * 10.-classpath <路径> 指定查找用户类文件的位置
		 * 
		 * 11.-cp <路径> 指定查找用户类文件的位置
		 * 
		 * 12.-sourcepath <路径> 指定查找输入源文件的位置
		 * 
		 * 13.-bootclasspath <路径> 覆盖引导类文件的位置
		 * 
		 * 14.-extdirs <目录> 覆盖安装的扩展目录的位置
		 * 
		 * 15.-endorseddirs <目录> 覆盖签名的标准路径的位置
		 * 
		 * 16.-d <目录> 指定存放生成的类文件的位置
		 * 
		 * 17.-encoding <编码> 指定源文件使用的字符编码
		 * 
		 * 18.-source <版本> 提供与指定版本的源兼容性
		 * 
		 * 19.-target <版本> 生成特定 VM 版本的类文件
		 * 
		 * 20.-version 版本信息
		 * 
		 * 21.-help 输出标准选项的提要
		 * 
		 * 22.-X 输出非标准选项的提要
		 * 
		 * 23.-J<标志> 直接将 <标志> 传递给运行时系统
		 */

		List<String> options = new ArrayList<String>();
		options.add("-encoding");
		options.add("UTF-8");
		options.add("-cp");
		options.add(FileUtil.getInstance().getClassesPhysicPath());
		JavaCompiler.CompilationTask task = compiler.getTask(null, fileManager,
				diagnostics, options, null, jfiles);
		boolean success = task.call();
		if (success) {
			JavaClassFileObject jco = fileManager.getJavaClassObject();
			DynamicClassLoader dynamicClassLoader = new DynamicClassLoader(
					this.classLoader);
			Class<?> clazz = dynamicClassLoader.loadClass(fullClassName, jco);
			instance = clazz.newInstance();
		} else {
			String error = "";
			for (Diagnostic<?> diagnostic : diagnostics.getDiagnostics()) {
				error = error + compilePrint(diagnostic);
				System.out.println(error);
			}
		}
		long end = System.currentTimeMillis();
		System.out.println("javaCodeToObject use:" + (end - start) + "ms");
		return instance;
	}

	private String compilePrint(Diagnostic<?> diagnostic) {
		System.out.println("Code:" + diagnostic.getCode());
		System.out.println("Kind:" + diagnostic.getKind());
		System.out.println("Position:" + diagnostic.getPosition());
		System.out.println("Start Position:" + diagnostic.getStartPosition());
		System.out.println("End Position:" + diagnostic.getEndPosition());
		System.out.println("Source:" + diagnostic.getSource());
		System.out.println("Message:" + diagnostic.getMessage(null));
		System.out.println("LineNumber:" + diagnostic.getLineNumber());
		System.out.println("ColumnNumber:" + diagnostic.getColumnNumber());
		StringBuffer res = new StringBuffer();
		res.append("Code:[" + diagnostic.getCode() + "]\n");
		res.append("Kind:[" + diagnostic.getKind() + "]\n");
		res.append("Position:[" + diagnostic.getPosition() + "]\n");
		res.append("Start Position:[" + diagnostic.getStartPosition() + "]\n");
		res.append("End Position:[" + diagnostic.getEndPosition() + "]\n");
		res.append("Source:[" + diagnostic.getSource() + "]\n");
		res.append("Message:[" + diagnostic.getMessage(null) + "]\n");
		res.append("LineNumber:[" + diagnostic.getLineNumber() + "]\n");
		res.append("ColumnNumber:[" + diagnostic.getColumnNumber() + "]\n");
		return res.toString();
	}
}
